﻿using System;
using System.Collections.Generic;

namespace ABA
{
    #region EventInfo
    public abstract class IEventInfo
    {
        public string listenerId;
    }

    public class EventInfo : IEventInfo
    {
        public Action actions;

        public EventInfo(Action action, string id)
        {
            actions = action;
            listenerId = id;
        }
    }

    public class EventInfo<T> : IEventInfo
    {    
        public Action<T> actions;

        public EventInfo(Action<T> action, string id)
        {
            actions = action;
            listenerId = id;
        }
    }

    public class EventInfo<T, U> : IEventInfo
    {       
        public Action<T, U> actions;

        public EventInfo(Action<T, U> action, string id)
        {
            actions = action;
            listenerId = id;            
        }
    }

    public class EventInfo<T, U, W> : IEventInfo
    {       
        public Action<T, U, W> actions;

        public EventInfo(Action<T, U, W> action, string id)
        {
            actions = action;
            listenerId = id;
        }
    }
    #endregion

    /// <summary>
    /// 总观察者 - 总事件中心系统
    /// </summary>
    public class Event : Singleton<Event>
    {
        /// <summary>
        ///  Dictionary Key->Int.32 Value->EventInfo,调用频率高建议使用int事件，减少字典内String的哈希碰撞
        /// </summary>
        private Dictionary<int, List<IEventInfo>> m_eventDic = new Dictionary<int, List<IEventInfo>>();

        /// <summary>
        ///  Dictionary Key->string Value->EventInfo,调用频率不高的时候可以使用
        /// </summary>
        private Dictionary<string, List<IEventInfo>> m_eventStrDic = new Dictionary<string, List<IEventInfo>>();

        #region AddListener
        public void AddListener<T>(int eventid, string listenerId, Action<T> action)
        {
            if (!m_eventDic.ContainsKey(eventid))
            {
                List<IEventInfo> list = new List<IEventInfo>();
                m_eventDic.Add(eventid, list);
            }

            IEventInfo obj = m_eventDic[eventid].Find(c => c.listenerId == listenerId);
            if(obj != null){
                return;
            }
            m_eventDic[eventid].Add(new EventInfo<T>(action, listenerId));
        }

        public void AddListener<T, U>(int eventid, string listenerId, Action<T, U> action)
        {
            if (!m_eventDic.ContainsKey(eventid))
            {
                List<IEventInfo> list = new List<IEventInfo>();
                m_eventDic.Add(eventid, list);
            }

            IEventInfo obj = m_eventDic[eventid].Find(c => c.listenerId == listenerId);
            if(obj != null){
                return;
            }
            m_eventDic[eventid].Add(new EventInfo<T,U>(action, listenerId));

        }

        public void AddListener<T, U, W>(int eventid, string listenerId, Action<T, U, W> action)
        {
            if (!m_eventDic.ContainsKey(eventid))
            {
                List<IEventInfo> list = new List<IEventInfo>();
                m_eventDic.Add(eventid, list);
            }

            IEventInfo obj = m_eventDic[eventid].Find(c => c.listenerId == listenerId);
            if(obj != null){
                return;
            }
            m_eventDic[eventid].Add(new EventInfo<T,U,W>(action, listenerId));
        }

        public void AddListener(int eventid, string listenerId, Action action)
        {
            if (!m_eventDic.ContainsKey(eventid))
            {
                List<IEventInfo> list = new List<IEventInfo>();
                m_eventDic.Add(eventid, list);
            }

            IEventInfo obj = m_eventDic[eventid].Find(c => c.listenerId == listenerId);
            if(obj != null){
                return;
            }
            m_eventDic[eventid].Add(new EventInfo(action, listenerId));
        }
        #endregion

        #region RemoveListener
        public void RemoveListener<T>(int eventid, string listenerId)
        {
            if(m_eventDic.ContainsKey(eventid))
            {
                EventInfo<T> obj = m_eventDic[eventid].Find(c => c.listenerId == listenerId) as EventInfo<T>;
                if(obj != null){
                    obj.actions = null;
                    m_eventDic[eventid].Remove(obj);
                }
            }
        }
        public void RemoveListener<T,U>(int eventid, string listenerId)
        {
            if(m_eventDic.ContainsKey(eventid))
            {
                EventInfo<T,U> obj = m_eventDic[eventid].Find(c => c.listenerId == listenerId) as EventInfo<T,U>;
                if(obj != null){
                    obj.actions = null;
                    m_eventDic[eventid].Remove(obj);
                }
            }
        }
        public void RemoveListener<T,U,W>(int eventid, string listenerId)
        {
            if(m_eventDic.ContainsKey(eventid))
            {
                EventInfo<T,U,W> obj = m_eventDic[eventid].Find(c => c.listenerId == listenerId) as EventInfo<T,U,W>;
                if(obj != null){
                    obj.actions = null;
                    m_eventDic[eventid].Remove(obj);
                }
            }
        }
        public void RemoveListener(int eventid, string listenerId)
        {
            if(m_eventDic.ContainsKey(eventid))
            {
                EventInfo obj = m_eventDic[eventid].Find(c => c.listenerId == listenerId) as EventInfo;
                if(obj != null){
                    obj.actions = null;
                    m_eventDic[eventid].Remove(obj);
                }
            }
        }
        #endregion

        #region Send
        public void Send<T>(int eventid, T info)
        {
            if (m_eventDic.ContainsKey(eventid))
            {
                foreach (var item in m_eventDic[eventid])
                {
                    var eventInfo = (item as EventInfo<T>);
                    if (eventInfo != null)
                    {
                        eventInfo.actions.Invoke(info);
                    }
                }
            }
        }

        public void Send<T, U>(int eventid, T info, U info2)
        {
            if (m_eventDic.ContainsKey(eventid))
            {
                foreach (var item in m_eventDic[eventid])
                {
                    var eventInfo = (item as EventInfo<T,U>);
                    if (eventInfo != null)
                    {
                        eventInfo.actions.Invoke(info,info2);
                    }
                }
            }
        }

        public void Send<T, U, W>(int eventid, T info, U info2, W info3)
        {
            if (m_eventDic.ContainsKey(eventid))
            {
                foreach (var item in m_eventDic[eventid])
                {
                    var eventInfo = (item as EventInfo<T,U,W>);
                    if (eventInfo != null)
                    {
                        eventInfo.actions.Invoke(info,info2,info3);
                    }
                }
            }
        }

        /// <summary>
        /// 事件触发 无参
        /// </summary>
        /// <param name="name"></param>
        public void Send(int eventid)
        {
            if (m_eventDic.ContainsKey(eventid))
            {
                foreach (var item in m_eventDic[eventid])
                {
                    var eventInfo = (item as EventInfo);
                    if (eventInfo != null)
                    {
                        eventInfo.actions.Invoke();
                    }
                }
            }

        }
        #endregion

        #region StringEvent
        #region AddListener
        public void AddListener<T>(string eventid, string listenerId, Action<T> action)
        {
            if (!m_eventStrDic.ContainsKey(eventid))
            {
                List<IEventInfo> list = new List<IEventInfo>();
                m_eventStrDic.Add(eventid, list);
            }

            IEventInfo obj = m_eventStrDic[eventid].Find(c => c.listenerId == listenerId);
            if(obj != null){
                return;
            }
            m_eventStrDic[eventid].Add(new EventInfo<T>(action, listenerId));
        }

        public void AddListener<T, U>(string eventid, string listenerId, Action<T, U> action)
        {
            if (!m_eventStrDic.ContainsKey(eventid))
            {
                List<IEventInfo> list = new List<IEventInfo>();
                m_eventStrDic.Add(eventid, list);
            }

            IEventInfo obj = m_eventStrDic[eventid].Find(c => c.listenerId == listenerId);
            if(obj != null){
                return;
            }
            m_eventStrDic[eventid].Add(new EventInfo<T,U>(action, listenerId));
        }

        public void AddListener<T, U, W>(string eventid, string listenerId, Action<T, U, W> action)
        {
            if (!m_eventStrDic.ContainsKey(eventid))
            {
                List<IEventInfo> list = new List<IEventInfo>();
                m_eventStrDic.Add(eventid, list);
            }

            IEventInfo obj = m_eventStrDic[eventid].Find(c => c.listenerId == listenerId);
            if(obj != null){
                return;
            }
            m_eventStrDic[eventid].Add(new EventInfo<T,U,W>(action, listenerId));
        }

        public void AddListener(string eventid, string listenerId, Action action)
        {
            if (!m_eventStrDic.ContainsKey(eventid))
            {
                List<IEventInfo> list = new List<IEventInfo>();
                m_eventStrDic.Add(eventid, list);
            }

            IEventInfo obj = m_eventStrDic[eventid].Find(c => c.listenerId == listenerId);
            if(obj != null){
                return;
            }
            m_eventStrDic[eventid].Add(new EventInfo(action, listenerId));
        }
        #endregion

        #region RemoveListener
        public void RemoveListener<T>(string eventid, string listenerId)
        {
            if(m_eventStrDic.ContainsKey(eventid))
            {
                EventInfo<T> obj = m_eventStrDic[eventid].Find(c => c.listenerId == listenerId) as EventInfo<T>;
                if(obj != null){
                    obj.actions = null;
                    m_eventStrDic[eventid].Remove(obj);
                }
            }
        }

        public void RemoveListener<T,U>(string eventid, string listenerId)
        {
            if(m_eventStrDic.ContainsKey(eventid))
            {
                EventInfo<T,U> obj = m_eventStrDic[eventid].Find(c => c.listenerId == listenerId) as EventInfo<T,U>;
                if(obj != null){
                    obj.actions = null;
                    m_eventStrDic[eventid].Remove(obj);
                }
            }
        }

        public void RemoveListener<T,U,W>(string eventid, string listenerId)
        {
            if(m_eventStrDic.ContainsKey(eventid))
            {
                EventInfo<T,U,W> obj = m_eventStrDic[eventid].Find(c => c.listenerId == listenerId) as EventInfo<T,U,W>;
                if(obj != null){
                    obj.actions = null;
                    m_eventStrDic[eventid].Remove(obj);
                }
            }
        }
        public void RemoveListener(string eventid, string listenerId)
        {
            if(m_eventStrDic.ContainsKey(eventid))
            {
                EventInfo obj = m_eventStrDic[eventid].Find(c => c.listenerId == listenerId) as EventInfo;
                if(obj != null){
                    obj.actions = null;
                    m_eventStrDic[eventid].Remove(obj);
                }
            }
        }
        #endregion

        #region Send
        public void Send<T>(string eventid, T info)
        {
            if (m_eventStrDic.ContainsKey(eventid))
            {
                foreach (var item in m_eventStrDic[eventid])
                {
                    var eventInfo = (item as EventInfo<T>);
                    if (eventInfo != null)
                    {
                        eventInfo.actions.Invoke(info);
                    }
                }
            }
        }

        public void Send<T, U>(string eventid, T info, U info2)
        {
            if (m_eventStrDic.ContainsKey(eventid))
            {
                foreach (var item in m_eventStrDic[eventid])
                {
                    var eventInfo = (item as EventInfo<T,U>);
                    if (eventInfo != null)
                    {
                        eventInfo.actions.Invoke(info,info2);
                    }
                }
            }
        }

        public void Send<T, U, W>(string eventid, T info, U info2, W info3)
        {
            if (m_eventStrDic.ContainsKey(eventid))
            {
                foreach (var item in m_eventStrDic[eventid])
                {
                    var eventInfo = (item as EventInfo<T,U,W>);
                    if (eventInfo != null)
                    {
                        eventInfo.actions.Invoke(info,info2,info3);
                    }
                }
            }
        }

        public void Send(string eventid)
        {
            if (m_eventStrDic.ContainsKey(eventid))
            {
                foreach (var item in m_eventStrDic[eventid])
                {
                    var eventInfo = (item as EventInfo);
                    if (eventInfo != null)
                    {
                        eventInfo.actions.Invoke();
                    }
                }
            }
        }
        #endregion
        #endregion

        #region Clear
        public void Clear()
        {
            m_eventDic.Clear();
            m_eventStrDic.Clear();
        }
        #endregion
    }


    public class StringId
    {
        private static Dictionary<string, int> m_eventTypeHashMap = new Dictionary<string, int>();
        private static Dictionary<int, string> m_eventHashToStringMap = new Dictionary<int, string>();
        private static int m_currentId = 0;

        public static int StringToHash(string val)
        {
            int hashId;
            if (m_eventTypeHashMap.TryGetValue(val, out hashId))
            {
                return hashId;
            }

            hashId = ++m_currentId;
            m_eventTypeHashMap[val] = hashId;
            m_eventHashToStringMap[hashId] = val;

            return hashId;
        }

        public static string HashToString(int hash)
        {
            string value;
            if (m_eventHashToStringMap.TryGetValue(hash, out value))
            {
                return value;
            }
            return string.Empty;
        }
    }

}